package com.thd.springboottest.security.config;

import com.thd.springboottest.security.securitycustom.JwtTokenFilter;
import com.thd.springboottest.security.securitycustom.MyPasswordEncoder;
import com.thd.springboottest.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.ForwardLogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;

/**
 * com.thd.springboottest.security.config.SecurityConfig
 *
 * @author: wanglei62
 * @DATE: 2022/6/21 16:38
 **/

@EnableGlobalMethodSecurity(prePostEnabled = true)   // 开启方法注解鉴权
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 自定义登录成功处理器
    @Autowired
    private AuthenticationSuccessHandler myAuthenticationSuccessHandler;

    // 自定义登录失败处理器
    @Autowired
    private AuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private AccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private AuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private LogoutHandler myLogoutHandler;
    @Autowired
    private UserService userService;
    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private SecurityThirdConfig securityThirdConfig;

    /**
     * 密码加密工具，主要的两个功能：
     *  1.PasswordEncoder.encode()  : 用于创建用户时对密码的加密，将加密后的字符串保存到数据库
     *  2.PasswordEncoder.match(): 用于对比用户登录时输入的密码与数据库保存的已加密的密码是否一致( 现将用户输入的密码加密，然后与数据库中加密后的字符串比较是否一致)
     */
    @Bean
    public PasswordEncoder MyPasswordEncoder(){
        return new MyPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {


        http
            .formLogin()//
                .disable() // 不使用表单登录
            .addFilterAfter(new JwtTokenFilter( userService), UsernamePasswordAuthenticationFilter.class) // 添加自定义的过滤器
            .apply(securityThirdConfig) // 自定义的登录
                .and()
            .logout()// 登出的配置
                .addLogoutHandler(new SecurityContextLogoutHandler()) // 配置登出处理器,可通过实现LogoutHandler接口进行自定义
                .logoutSuccessHandler(new ForwardLogoutSuccessHandler("/loginsuccess")) // 配置登出成功后的处理器,可通过实现LogoutSuccessHandler接口进行自定义
                .and()

            .exceptionHandling() //添加自定义未授权和未登录结果返回
                .accessDeniedHandler(myAccessDeniedHandler) //添加访问未经授权的接口的处理,可通过实现AccessDeniedHandler接口进行自定义
                .authenticationEntryPoint(myAuthenticationEntryPoint) //添加访问接口时未登录的处理,可通过实现AuthenticationEntryPoint接口进行自定义
                .and()

            .authorizeRequests() // 对请求授权
                .antMatchers("/login","/security/test","/security/login").permitAll()    /* 设置那些路径不需要认证可以访问 */
                .antMatchers("/test/hello").hasAnyRole("manager,monitor")   /* 跟具已有角色判断是否可以访问这个页面 */
//                .antMatchers("/test/hello").hasRole("manager")     /* 跟具已有角色判断是否可以访问这个页面 */
//                .antMatchers("/test/hello").hasAnyAuthority("admin","manager") /* 只要有拥有其中一个权限即可访问这个页面 */
//                .antMatchers("/test/hello").hasAuthority("admin")    /* 当前用户登录只有具有admin权限才可以访问这个路径 */
                .anyRequest().authenticated()/* 其他所有请求都必须登录后才可以访问 */
//                ;
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);// springsecurity 不保存session，用于前后端分离项目  基于token，不需要session , 如果是前后端不分离则去掉这段配置



        /**
         *
         *
         *
         * 自定义登录 - 生成jwtToken：
         * -- 邮箱登录
         * http://127.0.0.1:8899/thd/thirdLogin?userKey=13800138000&validatecode=123456
         * http://127.0.0.1:8899/thd/thirdLogin?userKey=13800138001&validatecode=123456
         *
         * -- 用户名密码登录
         * http://127.0.0.1:8899/thd/thirdLogin?userKey=wsl&validatecode=654321&loginType=USERNAMEPASSWORD
         * http://127.0.0.1:8899/thd/thirdLogin?userKey=zhangsan&validatecode=654321&loginType=USERNAMEPASSWORD
         *
         * 测试结果：
         *
         * http://127.0.0.1:8899/thd/security/test  不登录可以正常访问 （通过security的配置 不认证即可访问）
         *
         * http://127.0.0.1:8899/thd/security/info  （没有带mytoken）进入登录失败处理器
         *
         * http://127.0.0.1:8899/thd/security/info?mytoken=[jwtToken]  带有认证token 可以进入
         *
         * http://127.0.0.1:8899/thd/security/testRoleAdmin （没有带mytoken）进入登录失败处理器
         * http://127.0.0.1:8899/thd/security/testRoleAdmin?mytoken=[jwtToken]  ( 使用13800138001登录用户的jwtToken 认证通过，但没有权限)  no have permission
         * http://127.0.0.1:8899/thd/security/testRoleAdmin?mytoken=[jwtToken] ( 使用13800138000录用户的jwtToken)认证通过，且有权限，可以访问 SUCCESS
         *
         * http://127.0.0.1:8899/thd/security/testRoleAdminOrUser?mytoken=[jwtToken] (带有admin和user角色的可以进入)
         * http://127.0.0.1:8899/thd/security/testAuthorityDel?mytoken=[jwtToken] (带有del权限的可以进入)
         * http://127.0.0.1:8899/thd/security/testAuthorityDelOrQuery?mytoken=[jwtToken] (带有del和query权限的可以进入)
         * http://127.0.0.1:8899/thd/security/testAuthorityTree?mytoken=[jwtToken] (SpringSecurity不支持类似shiro的带有:和通配符的权限判断)
         *
         *
         * 前后端不分离测试
         * 1.先注释掉SecurityConfig的 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 开启session
         * 2.登录http://127.0.0.1:8899/thd/thirdLogin?userKey=13800138000&validatecode=123456
         * 3.直接访问 http://127.0.0.1:8899/thd/security/info 不要带入mytoken参数  可以看到刚才的登录人信息
         * 4.直接访问 http://127.0.0.1:8899/thd/security/testPreAuthorize 不要带入mytoken参数 如果刚才使用的13800138000登录的则可以直接访问
         *
         *
         */

    }
}
